/**
 *
 */
package com.cmcc.awareness;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pcap4j.packet.TcpPacket;
import org.pcap4j.packet.UdpPacket;

import jpcap.packet.TCPPacket;
import jpcap.packet.UDPPacket;


import com.google.common.hash.HashCode;

/**
 * @Date 2018年4月11日 上午10:27:27
 * @author Amish
 * 
 * (1)解析proto.dat
 * (2)提供业务识别入口方法和实现
 * (3)支持TCP、UDP
 * (4)Protocol feature code(协议特征码)
 *     1、[支持] 端口表达式：PORT。
 *     2、[支持] 报文长度(负载部分)表达式：l。[支持大于、小于、等于] ***
 *     3、[支持] 报文中字节与负载长度关系表达式：d。[ 不 支持负offset] ***
 *     4、[支持] 字节操作符表达式：b。[支持负offset] *
 *     5、[支持] 字节或操作符表达式：o。[支持负offset] *
 *     6、[支持] 字符串操作符表达式：s。[支持负offset][支持ASCII和Hex] **
 *     7、[支持] 单字节比较运算符表达式：c。[支持负offset] [支持大于、小于、不等于] *
 *     8、[不支持] 自定义比较运算符表达式：e。
 *     9、[支持] IP操作符表达式：SERVER/SERVER1。[支持SERVER][不支持SERVER1]
 *     10、[支持] 任意位置操作符表达式：x。[支持ASCII和Hex] **
 * 
 * 
 * 
 * 
 */
public class ProtoDat {
	private static Logger logger = LogManager.getLogger(ProtoDat.class);
	
//	private static ProtoDat protoDat = new ProtoDat();
//	public static ProtoDat getInstance(){//饿汉
//		return protoDat;
//	}
	public ProtoDat(String protodatPath){
		//protoDatApps = new HashMap<String,ProtoDatApp>();
		protoDatApps =  new ArrayList<ProtoDatApp>();
		this.parseFile(protodatPath);
	}
	
	//private HashMap<String,ProtoDatApp> protoDatApps;
	public ArrayList<ProtoDatApp> protoDatApps;
	/**
	 * 
	 * @param tcpPacket  (from jpcap)
	 * @return
	 */
	public AppInfo appDistinguish(jpcap.packet.TCPPacket tcpPacket,String protoListPath){

//		for(Entry<String, ProtoDatApp> entry : protoDatApps.entrySet()){
//			ProtoDatApp protoDatApp = entry.getValue();
//			String matchAppName = protoDatApp.distinguish("TCP", tcpPacket);
//			if(matchAppName != null){
//    			return ProtoList.getInstance().appInfos.get(matchAppName);
//    		}
//		}
		ProtoList protoList = new ProtoList(protoListPath);
		for(ProtoDatApp protoDatApp : protoDatApps){
			String matchAppName = protoDatApp.distinguish("TCP", tcpPacket);
			if(matchAppName != null){
    			return protoList.appInfos.get(matchAppName);
    		}
		}
		
		return null;
	}
	
	public AppInfo appDistinguish(TcpPacket tcpPacket,String protoListPath){

//		for(Entry<String, ProtoDatApp> entry : protoDatApps.entrySet()){
//			ProtoDatApp protoDatApp = entry.getValue();
//			String matchAppName = protoDatApp.distinguish("TCP", tcpPacket);
//			if(matchAppName != null){
//    			return ProtoList.getInstance().appInfos.get(matchAppName);
//    		}
//		}
		ProtoList protoList = new ProtoList(protoListPath);
		for(ProtoDatApp protoDatApp : protoDatApps){
			String matchAppName = protoDatApp.distinguish("TCP", tcpPacket);
			if(matchAppName != null){
    			return protoList.appInfos.get(matchAppName);
    		}
		}
		
		return null;
	}
	
	public Map<String,Object> appDistinguishWithRule(TcpPacket tcpPacket,String protoListPath){

//		for(Entry<String, ProtoDatApp> entry : protoDatApps.entrySet()){
//			ProtoDatApp protoDatApp = entry.getValue();
//			String matchAppName = protoDatApp.distinguish("TCP", tcpPacket);
//			if(matchAppName != null){
//    			return ProtoList.getInstance().appInfos.get(matchAppName);
//    		}
//		}
		Map<String,Object> result = new HashMap<>();
		ProtoList protoList = new ProtoList(protoListPath);
		for(ProtoDatApp protoDatApp : protoDatApps){
			ProtoDatProtocolFeatureCode protoDatProtocolFeatureCode = protoDatApp.distinguishWithRule("TCP", tcpPacket);
			if(protoDatProtocolFeatureCode != null){
				result.put("appInfo",protoList.appInfos.get(protoDatProtocolFeatureCode.appName));
				result.put("rule",protoDatProtocolFeatureCode);
    			return result;
    		}
		}
		
		return result;
	}
	
	/**
	 * 
	 * @param udpPacket  (from jpcap)
	 * @return
	 */
	public AppInfo appDistinguish(jpcap.packet.UDPPacket udpPacket,String protoListPath){
		
//        for(Entry<String, ProtoDatApp> entry : protoDatApps.entrySet()){
//        	ProtoDatApp protoDatApp = entry.getValue();
//			String matchAppName = protoDatApp.distinguish("UDP", udpPacket);
//			if(matchAppName != null){
//    			return ProtoList.getInstance().appInfos.get(matchAppName);
//    		}
//		}
		ProtoList protoList = new ProtoList(protoListPath);
		for(ProtoDatApp protoDatApp : protoDatApps){
			String matchAppName = protoDatApp.distinguish("UDP", udpPacket);
			if(matchAppName != null){
    			return protoList.appInfos.get(matchAppName);
    		}
		}
		
		return null;
	}
	
	public AppInfo appDistinguish(UdpPacket udpPacket,String protoListPath){
		
//      for(Entry<String, ProtoDatApp> entry : protoDatApps.entrySet()){
//      	ProtoDatApp protoDatApp = entry.getValue();
//			String matchAppName = protoDatApp.distinguish("UDP", udpPacket);
//			if(matchAppName != null){
//  			return ProtoList.getInstance().appInfos.get(matchAppName);
//  		}
//		}
		ProtoList protoList = new ProtoList(protoListPath);
		for(ProtoDatApp protoDatApp : protoDatApps){
			String matchAppName = protoDatApp.distinguish("UDP", udpPacket);
			if(matchAppName != null){
  			return protoList.appInfos.get(matchAppName);
  		}
		}
		
		return null;
	}
	
	public Map<String,Object> appDistinguishWithRule(UdpPacket udpPacket,String protoListPath){
		Map<String,Object> result = new HashMap<>();
		ProtoList protoList = new ProtoList(protoListPath);
		for(ProtoDatApp protoDatApp : protoDatApps){
			ProtoDatProtocolFeatureCode protoDatProtocolFeatureCode = protoDatApp.distinguishWithRule("UDP", udpPacket);
			if(protoDatProtocolFeatureCode != null){
				result.put("appInfo",protoList.appInfos.get(protoDatProtocolFeatureCode.appName));
				result.put("rule",protoDatProtocolFeatureCode);
    			return result;
    		}
		}
		return result;
	}
	
	private void parseFile(String protodatPath){
		String path = protodatPath;//ProtoList.class.getResource("/").getPath()+"config/proto.dat";
    	path = path.replace("%20", " ");
    	path = path.replace("%23", "#");
    	
    	File file = new File(path);
		Scanner sc = null;

		try {
			sc = new Scanner(new FileInputStream(file));
			String str = null;
			ArrayList<String> alTemp = new ArrayList<String>();
			
			int i = 0;
			
			while (sc.hasNextLine()) {
				i++;
				str = sc.nextLine().trim();
				if(str.equalsIgnoreCase("")){
					//dothing  过滤掉空行
				}else if(!str.equalsIgnoreCase("<END>")){
					alTemp.add(str);				
				}else{//读到<END>
					alTemp.add(str);//<END>也要写入
					ProtoDatApp protoDatApp = new ProtoDatApp();
					protoDatApp.parseProtoDatApp(alTemp);
					//protoDatApps.put(protoDatApp.appName, protoDatApp);
					protoDatApps.add(protoDatApp);
					alTemp.clear();//遇到<END>，清空List
				}
			}
			System.out.println(i);
		} catch (Exception e) {
			logger.error(e.getCause(),e);
		} finally {
			sc.close();
		}
	}
	
	public static void main(String[] args) throws UnknownHostException{
		
		
		ProtoDat p = new ProtoDat("G:\\项目材料\\2021-AiRules\\参考文档\\8.0 计划发布\\proto.dat");
		System.out.println("==========================================================");
		for(ProtoDatApp protoDatApp : p.protoDatApps){
			System.out.println(protoDatApp.toString());
		}
		
	    p.test(p);

	}
	
	public void test(ProtoDat p){
		try{
			String protoList = "D:\\workspace\\cmcc-common\\cmcc-common-awareness\\src\\main\\resources\\config\\org\\proto.list";
			System.out.println("#############################[ TEST START ]#############################");
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ TCP ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			TCPPacket tcpPacket;
			tcpPacket = new TCPPacket(1, 1, 0, 0, false, false, false, false, false, false, false, false, 0, 0);
			tcpPacket.dst_ip = InetAddress.getByName("192.168.0.1");
			tcpPacket.src_ip = InetAddress.getByName("192.168.0.2");
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test PORT ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			//PORT:19100|19101|19102|0
			tcpPacket = new TCPPacket(1, 19101, 0, 0, false, false, false, false, false, false, false, false, 0, 0);
			System.out.println("-----------------------------test PORT-1"+p.appDistinguish(tcpPacket,protoList));
			//PORT:19100|19101|19102|0
			tcpPacket = new TCPPacket(19102, 1, 0, 0, false, false, false, false, false, false, false, false, 0, 0);
			System.out.println("-----------------------------test PORT-2"+p.appDistinguish(tcpPacket,protoList));
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test SERVER ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			tcpPacket = new TCPPacket(1, 1, 0, 0, false, false, false, false, false, false, false, false, 0, 0);
			//SERVER:192.168.98.1/24|0
			tcpPacket.dst_ip = InetAddress.getByName("192.168.98.199");
			tcpPacket.src_ip = InetAddress.getByName("192.168.101.2");
			System.out.println("-----------------------------test SERGER-1"+p.appDistinguish(tcpPacket,protoList));
			//SERVER:192.168.99.1-192.168.99.10|0
			tcpPacket.dst_ip = InetAddress.getByName("192.168.101.1");
			tcpPacket.src_ip = InetAddress.getByName("192.168.99.9");
			System.out.println("-----------------------------test SERGER-2"+p.appDistinguish(tcpPacket,protoList));
			//SERVER:192.168.100.1:19000,19001|0
			tcpPacket = new TCPPacket(19000, 1, 0, 0, false, false, false, false, false, false, false, false, 0, 0);
			tcpPacket.dst_ip = InetAddress.getByName("192.168.101.1");
			tcpPacket.src_ip = InetAddress.getByName("192.168.100.1");
			System.out.println("-----------------------------test SERGER-3"+p.appDistinguish(tcpPacket,protoList));
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test l ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			tcpPacket = new TCPPacket(1, 1, 0, 0, false, false, false, false, false, false, false, false, 0, 0);
			tcpPacket.dst_ip = InetAddress.getByName("192.168.0.1");
			tcpPacket.src_ip = InetAddress.getByName("192.168.0.2");
			//{l|0|32}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".toLowerCase()).asBytes();
			System.out.println("-----------------------------test l-1"+p.appDistinguish(tcpPacket,protoList));
			//{l|2|2}
			tcpPacket.data = HashCode.fromString("01".toLowerCase()).asBytes();
			System.out.println("-----------------------------test l-2"+p.appDistinguish(tcpPacket,protoList));
			//{l|1|48}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01".toLowerCase()).asBytes();
			System.out.println("-----------------------------test l-3"+p.appDistinguish(tcpPacket,protoList));
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test d ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			//{d|0|0x110a}
			tcpPacket.data = HashCode.fromString("0623456789abcdef0123456789abcdef".toLowerCase()).asBytes();
			System.out.println("-----------------------------test d-1"+p.appDistinguish(tcpPacket,protoList));
			//{d|14|0x200}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123456789ab1000".toLowerCase()).asBytes();
			System.out.println("-----------------------------test d-2"+p.appDistinguish(tcpPacket,protoList));
			//{d|12|0x0406}
			tcpPacket.data = HashCode.fromString("0123456789abcdef012345670a000000".toLowerCase()).asBytes();
			System.out.println("-----------------------------test d-3"+p.appDistinguish(tcpPacket,protoList));
			
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test b ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			//{b|0|0xaa}
			tcpPacket.data = HashCode.fromString("aa23456789abcdef0123456789abcdef".toLowerCase()).asBytes();
			System.out.println("-----------------------------test b-1"+p.appDistinguish(tcpPacket,protoList));
			//{b|-1|0xaa}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123456789abcdaa".toLowerCase()).asBytes();
			System.out.println("-----------------------------test b-2"+p.appDistinguish(tcpPacket,protoList));
			//{b|0|0xbb}{b|-1|0xbb}
			tcpPacket.data = HashCode.fromString("bb23456789abcdef0123456789abcdbb".toLowerCase()).asBytes();
			System.out.println("-----------------------------test b-3"+p.appDistinguish(tcpPacket,protoList));
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test o ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			//{o|15|[0xcc,0xdd]}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123456789abcdcc".toLowerCase()).asBytes();
			System.out.println("-----------------------------test o-1"+p.appDistinguish(tcpPacket,protoList));
			//{o|-16|[0xcc,0xdd]}
			tcpPacket.data = HashCode.fromString("dd23456789abcdef0123456789abcdef".toLowerCase()).asBytes();
			System.out.println("-----------------------------test o-2"+p.appDistinguish(tcpPacket,protoList));
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test c ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			//{c|11|0x0aa}{l|0|17}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123456789abcdef01".toLowerCase()).asBytes();
			System.out.println("-----------------------------test c-1"+p.appDistinguish(tcpPacket,protoList));
			//{c|11|0x167}{l|0|18}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123456889abcdef0102".toLowerCase()).asBytes();
			System.out.println("-----------------------------test c-2"+p.appDistinguish(tcpPacket,protoList));
			//{c|11|0x267}{l|0|18}
			tcpPacket.data = HashCode.fromString("0123456789abcdef0123450089abcdef0102".toLowerCase()).asBytes();
			System.out.println("-----------------------------test c-3"+p.appDistinguish(tcpPacket,protoList));
			//{c|-11|0x20a}{l|0|18}
			tcpPacket.data = HashCode.fromString("0123456789abcd090123456789abcdef0102".toLowerCase()).asBytes();
			System.out.println("-----------------------------test c-4"+p.appDistinguish(tcpPacket,protoList));
			
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test s ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			//{s|-6|abcdef}{l|0|10}
			tcpPacket.data = HashCode.fromString("FFFFFFFF616263646566".toLowerCase()).asBytes();
			System.out.println("-----------------------------test s-1"+p.appDistinguish(tcpPacket,protoList));
			//{s|1|abcdef}{l|0|10}
			tcpPacket.data = HashCode.fromString("FF616263646566FFFFFF".toLowerCase()).asBytes();
			System.out.println("-----------------------------test s-2"+p.appDistinguish(tcpPacket,protoList));
			//{s|4|0x414243444546}{l|0|10}
			tcpPacket.data = HashCode.fromString("FFFFFFFF414243444546".toLowerCase()).asBytes();
			System.out.println("-----------------------------test s-3"+p.appDistinguish(tcpPacket,protoList));
			//{s|-8|0x414243444546}{l|0|10}
			tcpPacket.data = HashCode.fromString("FFFF414243444546FFFF".toLowerCase()).asBytes();
			System.out.println("-----------------------------test s-4"+p.appDistinguish(tcpPacket,protoList));
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test x ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			//{x|8|abcdefgh}{l|0|12}
			tcpPacket.data = HashCode.fromString("FF6162636465666768FFFFFF".toLowerCase()).asBytes();
			System.out.println("-----------------------------test x-1"+p.appDistinguish(tcpPacket,protoList));
			//{x|9|0x626364656667686970}{l|0|12}
			tcpPacket.data = HashCode.fromString("FF626364656667686970FFFF".toLowerCase()).asBytes();
			System.out.println("-----------------------------test x-2"+p.appDistinguish(tcpPacket,protoList));

			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ test complex ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			
			//PORT:19200|19201|19202|1
			//SERVER:192.168.200.1/24|1
			//{l|0|40}{d|0|0x120a}{b|-5|0x56}{o|-2|[0x58,0x59,0x5a]}{c|20|0x202}{x|3|WXY}{s|-6|UVWXYZ}
			tcpPacket = new TCPPacket(1, 19202, 0, 0, false, false, false, false, false, false, false, false, 0, 0);
			tcpPacket.dst_ip = InetAddress.getByName("192.168.200.100");
			tcpPacket.src_ip = InetAddress.getByName("192.168.0.2");
			tcpPacket.data = HashCode.fromString("001eFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01FFFFFFFFFFFFFFFFFFFFFFFFFF55565758595a".toLowerCase()).asBytes();
			System.out.println("-----------------------------test complex-1"+p.appDistinguish(tcpPacket,protoList));
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>[ UDP ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
			UDPPacket udpPacket = new UDPPacket(4, 4672);
			udpPacket.data = HashCode.fromString("ffff41424344454647ffff".toLowerCase()).asBytes();//Lvpin:{x|7|0x41424344454647}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}
	
}
